home *** CD-ROM | disk | FTP | other *** search
- /*****
- *
- * Grant's CGI Framework
- * (Common Grant Interface :-)
- * by Grant Neufeld
- * http://arpp.carleton.ca/grant/mac/grantscgi/
- *
- * CGI.c
- *
- * Standard functions for cgi applications.
- *
- * You must call InitCGIUtil in your application startup.
- * You must install CGIAEHandle as the event handler for the WWWΩsdoc apple event
- * You must write the function:
- * void CustomCGIProcess ( CGIHdl theCGIHdl )
- * which is where you will, guess what, do your application specific processing
- * of the cgi stuff.
- *
- * Do not call any functions begining with lower case 'cgi' - you can use any of the
- * others - but read their comments first for details.
- *
- * watch the homepage for future upgrades
- *
- *
- * Copyright ©1995,1996 by Grant Neufeld
- *
- * http://arpp.carleton.ca/grant/
- * gneufeld@ccs.carleton.ca
- * grant@acm.org
- * grant@kagi.com
- *
- * This source may be freely used as long as the copyright notice is kept in the source.
- * I ask that you let me know of any enhancements (read: bug fixes) to this code.
- * I would also like copies of (or discounts on) anything you produce this with, please.
- *
- * See the License and Limited Warranty Agreement for all the legal stuff.
- *
- *****/
-
- #include "MyConfiguration.h"
- #if kCompileWithCGICode
-
- #include <stdio.h>
- #include <string.h>
- #if __profile__ && __MWERKS__
- #include <Profiler.h>
- #endif
-
- #include "compiler_stuff.h"
- #include "globals.h"
-
- #include "AEFunc.h"
- #include "DebugUtil.h"
- #include "LogUtil.h"
- #include "MemoryUtil.h"
- #include "ProcessUtil.h"
- #include "Quit.h"
- #include "StringUtil.h"
-
- /* CGI.h processes differently for CGI.c, this is controlled by defining __CGISegment__ */
- #define __CGISegment__ 1
- #include "CGI.h"
- #undef __CGISegment__
-
-
- /*** CONSTANT DECLARATIONS ***/
-
- #define kHTTPHeaderStrs 3000
- #define kHTTPHeaderOK 1
- #define kHTTPHeaderRedirect 2
- #define kHTTPHeaderErr 3
- #define kHTTPHeaderPush 4
-
-
- /*** LOCAL VARIABLES ***/
-
- #if kCompileWithThreadedAppleEvents
- static AEEventHandlerUPP vCGIAEResumeCompleteUPP;
- #endif
-
- static char * vNULLString;
-
-
- /*** LOCAL FUNCTION PROTOTYPES ***/
-
- static void cgiDisposeHandle ( CGIHdl );
-
- static OSErr cgiAESearchDocProcess ( CGIHdl );
- static OSErr cgiAEComplete ( CGIHdl );
-
- #if kCompileWithThreadedAppleEvents
- pascal void * CGIAESearchDocProcessThread ( void * );
- pascal OSErr CGIAEResumeComplete ( const AppleEvent *, AppleEvent *, long );
- #endif
-
- #if kCompileWithCGImethod
- static OSErr cgiAEGetParamHTTPMethod ( const AppleEvent *, AEKeyword, HTTPMethod *, char *, long );
- #endif
-
- static void cgiPostProcess ( CGIHdl );
-
-
- /*** FUNCTIONS ***/
-
- /* This initialization function MUST be called in the startup sequence of your
- application if you use any of the CGI functions (including the AppleEvent) */
- OSErr
- InitCGIUtil ( void )
- {
- OSErr theErr;
- AEEventHandlerUPP theUPP;
-
- /* initialize the null string to consist of one character '\0' */
- vNULLString = (char *) MemoryNewPtr ( 1, &theErr );
- if ( vNULLString != NULL )
- {
- vNULLString[0] = nil;
- }
- else
- {
- /* IMPORTANT: we failed initialization
- if we couldn't allocate a one byte string! */
- if ( theErr == noErr )
- {
- /* make sure there's an error value */
- theErr = memFullErr;
- }
-
- return theErr;
- }
-
- GetIndString ( gHTTPHeaderOK, kHTTPHeaderStrs, kHTTPHeaderOK );
- if ( gHTTPHeaderOK[nil] != nil )
- {
- P2CStr ( gHTTPHeaderOK );
- gHTTPHeaderOKSize = strlen ( (char *)gHTTPHeaderOK );
- }
-
- GetIndString ( gHTTPHeaderRedirect, kHTTPHeaderStrs, kHTTPHeaderRedirect );
- if ( gHTTPHeaderRedirect[nil] != nil )
- {
- P2CStr ( gHTTPHeaderRedirect );
- gHTTPHeaderRedirectSize = strlen ( (char *)gHTTPHeaderRedirect );
- }
-
- GetIndString ( gHTTPHeaderErr, kHTTPHeaderStrs, kHTTPHeaderErr );
- if ( gHTTPHeaderErr[nil] != nil )
- {
- P2CStr ( gHTTPHeaderErr );
- gHTTPHeaderErrSize = strlen ( (char *)gHTTPHeaderErr );
- }
-
- #if kCompileWithCGISendPartial
- GetIndString ( gHTTPHeaderPush, kHTTPHeaderStrs, kHTTPHeaderPush );
- if ( gHTTPHeaderPush[nil] != nil )
- {
- P2CStr ( gHTTPHeaderPush );
- gHTTPHeaderPushSize = strlen ( (char *)gHTTPHeaderPush );
- }
- #endif
-
- /* it's okay to 'lose track' of theUPP since we never want to get rid of it
- until the application quits - at which point it will automatically be
- disposed of, anyway. */
- theUPP = NewAEEventHandlerProc ( CGIAESearchDoc );
- theErr = AEInstallEventHandler ( kAEClassCGI, kAEIDSearchDoc, theUPP, 0L, false );
-
- #if kCompileWithThreadedAppleEvents
- vCGIAEResumeCompleteUPP = NewAEEventHandlerProc ( CGIAEResumeComplete );
- #endif
-
- return theErr;
- } /* InitCGIUtil */
-
-
- /** FORM FIELDS **/
- #pragma mark -
- #if kCompileWithCGIFormHandling
-
- /* The separator '&' separates individual fields.
- The delimiter '=' delimits the name and value in a field.
- For example: "Field 1=some stuff&Another Field=more stuff&Last Field=no stuff"
- Means that there are 3 fields with names "Field 1", "Another Field" and "Last Field"
-
- The function returns an array of field records with the last containing null values.
-
- You generally shouldn't call this function outside this file.
- Use it at your own risk. */
- /* ••• I should add error reporting */
- p_export
- CGIFormField *
- CGIFormFieldsFromArgs ( char *theString, long *count, short *outErr )
- {
- CGIFormField * theFields;
- long totalStrSize;
- long totalFields;
- long nameSize;
- long valueSize;
- long currentField;
- char * theStringPtr;
- char * fieldSeparator;
- char * fieldDelimiter;
-
- my_assert ( theString != NULL, "\pCGIFormArgs: nil string" );
-
- theFields = NULL;
-
- /* don't return number of fields until function is successful */
- *count = nil;
-
- totalStrSize = strlen ( theString );
-
- /* the total number of fields is the number of separator characters + 1 */
- totalFields = StringCountChar ( theString, kCGIFormFieldSeparator ) + 1;
- if ( totalFields == 1 )
- {
- /* the case where there were no separator characters is special,
- test for a field delimiter to confirm that the string passed does
- indeed contain field information */
- fieldDelimiter = StringChar ( theString, kCGIFormFieldDelimiter );
- if ( fieldDelimiter == NULL )
- {
- /* string does not contain field data */
- *outErr = 1; /* • might need better error here */
-
- goto Exit_Fail;
- }
- }
-
- theFields = (CGIFormField *) MemoryNewPtr ( ((totalFields + 1) * sizeof(CGIFormField)), outErr );
- if ( theFields == NULL )
- {
- /* memory didn't allocate */
- goto Exit_Fail;
- }
-
- /* set the name and value of the last field in the array to nil */
- (theFields[totalFields]).name = NULL;
- (theFields[totalFields]).value = NULL;
-
- theStringPtr = theString;
-
- for ( currentField = nil; currentField < totalFields; currentField++ )
- {
- /* set the name and value of the current field in the array to nil.
- this is to handle errors. */
- (theFields[currentField]).name = NULL;
- (theFields[currentField]).value = NULL;
-
- fieldDelimiter = StringChar ( theStringPtr, kCGIFormFieldDelimiter );
- fieldSeparator = StringChar ( theStringPtr, kCGIFormFieldSeparator );
-
- /* if there is a field delimiter, and it is before any field separator */
- if ( (fieldDelimiter != NULL) && ((fieldSeparator > fieldDelimiter) ||
- (fieldSeparator == NULL)) )
- {
- /* field name */
- /* the size of the name string is the difference between the begining
- of the field and the position of the field delimiter */
- nameSize = fieldDelimiter - theStringPtr;
-
- /* allocate the name string */
- (theFields[currentField]).name = (char *) MemoryNewPtr ( nameSize + 1, outErr );
- if ( (theFields[currentField]).name == NULL )
- {
- /* memory didn't allocate */
- (theFields[currentField]).value = NULL;
-
- goto Exit_Fail;
- }
-
- /* copy the field name */
- BlockMove ( theStringPtr, (theFields[currentField]).name, nameSize );
- /* null terminate the end of the name string */
- ((theFields[currentField]).name)[nameSize] = nil;
- /* convert the url encoded text to a normal string */
- CGIDecodeURLChars ( (theFields[currentField]).name );
-
- /* field value */
- if ( fieldSeparator != NULL )
- {
- valueSize = fieldSeparator - (fieldDelimiter + 1);
- }
- else
- {
- valueSize = strlen ( fieldDelimiter + 1 );
- }
-
- (theFields[currentField]).value = (char *) MemoryNewPtr ( (valueSize + 1), outErr );
- if ( (theFields[currentField]).value == NULL )
- {
- /* memory didn't allocate */
- DisposePtr ( (theFields[currentField]).name );
- (theFields[currentField]).name = NULL;
-
- goto Exit_Fail;
- }
-
- BlockMove ( fieldDelimiter + 1, (theFields[currentField]).value, valueSize );
- ((theFields[currentField]).value)[valueSize] = nil;
- CGIDecodeURLChars ( (theFields[currentField]).value );
-
- theStringPtr = fieldSeparator + 1;
- }
- else
- {
- /* invalid data encountered */
- *outErr = 2; /* •• need better error value */
-
- goto Exit_Fail;
- }
- }
-
- /* assign the return parameters values */
- *count = totalFields;
- *outErr = noErr;
-
- return theFields;
-
-
- Exit_Fail:
-
- if ( theFields != NULL )
- {
- /* release allocated memory */
- CGIFormFieldsDispose ( theFields );
- }
-
- return NULL;
- } /* CGIFormFieldsFromArgs */
-
-
- /* Returns a pointer to the first form field record, in the given fieldArray,
- that's name matches the supplied field name.
- [modified for 1.0b4 - use CGIHdl as parameter instead of CGIFormField] */
- p_export
- CGIFormField *
- CGIFormFieldsFindRecord ( CGIHdl theCGIHdl, const char *fieldName )
- {
- CGIFormField *fieldArray;
- long currentField;
- short stringDifference;
-
- my_assert ( theCGIHdl != NULL, "\pCGIFormFieldsFindRecord: theCGIHdl is NULL" );
- my_assert ( fieldName != NULL, "\pCGIFormFieldsFindRecord: fieldName is NULL" );
-
- fieldArray = (*theCGIHdl)->formFields;
- if ( (*theCGIHdl)->formFields == NULL )
- {
- return NULL;
- }
-
- /* look til we find something or we hit the end */
- for ( currentField = nil; (fieldArray[currentField]).name != NULL; currentField++ )
- {
- stringDifference = strcmp ( (fieldArray[currentField]).name, fieldName );
- if ( stringDifference == nil )
- {
- /* found a match, so we're done */
- return &(fieldArray[currentField]);
- }
- }
-
- /* didn't find a match */
- return NULL;
- } /* CGIFormFieldsFindRecord */
-
-
- /* Returns a pointer to the string containing the value from the field specified
- by 'fieldName'. Returns a pointer to a null string ("") if the field is not
- found. The pointer points directly into the form fields array in theCGIHdl.
- IMPORTANT: Whatever you do - do not attempt to deallocate the string returned
- by this function!!! */
- p_export
- const char *
- CGIFormFieldsFindValue ( CGIHdl theCGIHdl, const char *fieldName )
- {
- CGIFormField * theField;
-
- my_assert ( theCGIHdl != NULL, "\pCGIFormFieldsFindValue: theCGIHdl is NULL" );
- my_assert ( fieldName != NULL, "\pCGIFormFieldsFindValue: fieldName is NULL" );
-
- theField = CGIFormFieldsFindRecord ( theCGIHdl, fieldName );
- if ( (theField == NULL) || (theField->value == NULL) )
- {
- return vNULLString;
- }
- else
- {
- return theField->value;
- }
- } /* CGIFormFieldsFindValue */
-
-
- /* Deallocate memory for theFields array.
- You generally shouldn't call this function outside this file.
- Use it at your own risk. */
- void
- CGIFormFieldsDispose ( CGIFormField *theFields )
- {
- long offset;
-
- my_assert ( theFields != NULL, "\pCGIFormFieldsDispose: null field array pointer" );
-
- offset = nil;
-
- do
- {
- if ( (theFields[offset]).name != NULL )
- {
- /* if there's a name string, deallocate its memory */
- DisposePtr ( (Ptr)((theFields[offset]).name) );
-
- if ( (theFields[offset]).value != NULL )
- {
- /* if there's a value string, deallocate its memory */
- DisposePtr ( (Ptr)((theFields[offset]).value) );
- }
- }
-
- offset++;
- } while ( (theFields[offset]).name != NULL );
-
- DisposePtr ( (Ptr)theFields );
- } /* CGIFormFieldsDispose */
-
- #endif /* kCompileWithCGIFormHandling */
-
-
- /** ACTION SUPPORT **/
- #pragma mark -
- #if kCompileWithCGIActionSupport
-
- /* Returns true if the action parameter is either CGI or ACGI.
- Returns false if it is some other action. */
- p_export
- Boolean
- CGIActionIsCGIorACGI ( CGIHdl theCGIHdl )
- {
- int stringDifference;
-
- my_assert ( theCGIHdl != NULL, "\pCGIActionIsCGIorACGI: theCGIHdl is NULL" );
-
- /* is the action "CGI"? */
- stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameCGI );
- if ( stringDifference == nil )
- {
- return true;
- }
-
- /* is the action "ACGI"? */
- stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameACGI );
- if ( stringDifference == nil )
- {
- return true;
- }
-
- /* didn't find a match */
- return false;
- } /* CGIActionIsCGIorACGI */
-
- #endif /* kCompileWithCGIActionSupport */
-
-
- /** CHARACTER CODING **/
- #pragma mark -
-
- /* replaces instances of percent signs (%) followed by an ASCII char value
- with the actual character.
- This function modifies theString parameter! */
- p_export
- void
- CGIDecodeURLChars ( char *theString )
- {
- UInt32 read;
- UInt32 write;
- unsigned char theChar;
- unsigned char highOrder;
- unsigned char lowOrder;
- Boolean isValid;
-
- my_assert ( theString != NULL, "\pCGIDecodeURLChars: NULL string" );
-
- read = nil;
- write = nil;
-
- while ( theString[read] != nil )
- {
- switch ( theString[read] )
- {
- case '%':
- /* a percent symbol begins a hex char block (%## where ## is the hex value) */
-
- isValid = true;
-
- /* determine high order hex character */
- if ( (theString[read+1] >= 'A') && (theString[read+1] <= 'F') )
- {
- /* uppercase A-F convert to 10-15 */
- highOrder = theString[read+1] - 'A' + 10;
- }
- else if ( (theString[read+1] >= 'a') && (theString[read+1] <= 'f') )
- {
- /* lowercase a-f convert to 10-15 */
- highOrder = theString[read+1] - 'a' + 10;
- }
- else if ( (theString[read+1] >= '0') && (theString[read+1] <= '9') )
- {
- /* character digits 0-9 convert to decimal 0-9 */
- highOrder = theString[read+1] - '0';
- }
- else
- {
- /* Illegal character! Can't convert from hex */
- isValid = false;
- }
-
- /* Multiply high order hex digit by 16 */
- highOrder *= 16;
-
- /* determine low order hex character */
- if ( (theString[read+2] >= 'A') && (theString[read+2] <= 'F') )
- {
- /* uppercase A-F convert to 10-15 */
- lowOrder = (theString[read+2] - 'A' + 10);
- }
- else if ( (theString[read+2] >= 'a') && (theString[read+2] <= 'f') )
- {
- /* lowercase a-f convert to 10-15 */
- lowOrder = (theString[read+2] - 'a' + 10);
- }
- else if ( (theString[read+2] >= '0') && (theString[read+2] <= '9') )
- {
- /* character digits 0-9 convert to decimal 0-9 */
- lowOrder = (theString[read+2] - '0');
- }
- else
- {
- /* Illegal character! Can't convert from hex */
- isValid = false;
- }
-
- theChar = highOrder + lowOrder;
-
- if ( isValid )
- {
- isValid = (theChar >= 0) && (theChar < 256);
- }
-
- if ( isValid )
- {
- /* if theChar is valid, write it out */
-
- if ( theChar == 10 )
- {
- /* don't write newline */
- write--;
- }
- else
- {
- theString[write] = theChar;
- }
-
- /* Increment read past the two digits of the hex code */
- read += 2;
- }
- else
- {
- /* invalid hex character code, just write out the percent symbol */
- theString[write] = theString[read];
- }
- break;
-
- case '+':
- /* Plus symbols convert to space */
- theString[write] = ' ';
- break;
-
- case 10:
- /* ignore line feeds, we only need carriage returns (13) */
- write--;
- break;
-
- default:
- /* write out the character */
- theString[write] = theString[read];
- break;
- }
-
- read++;
- write++;
- }
-
- /* terminate the string */
- theString[write] = '\0';
- } /* CGIDecodeURLChars */
-
-
- /* • UPDATE 1996-03-10 •
- %hex encode all non-alphanumeric characters. IE. '~' (126) becomes '%7E'
- theString parameter is not modified.
- Will return NULL if unable to allocate memory for the encoded string.
- outErr will not be set if it is a NULL ptr. */
- p_export
- char *
- CGIEncodeURLChars ( const char *theString, OSErr *outErr )
- {
- char * theResult; /* new string containing the encoded form of theString */
- long strSize; /* size of the source string */
- Boolean charNeedsToBeHex; /* char needs to be encoded as hex */
- const char * stringLoc; /* current location in theString */
- char * resultLoc; /* current location in theResult string */
-
- my_assert ( theString != NULL, "\p: theString is NULL" );
-
- /* loop through the string finding out how big our final string will need to be.
- Add two extra bytes for every 'special' character. */
- strSize = nil;
- for ( stringLoc = theString; *stringLoc != nil; stringLoc++ )
- {
- charNeedsToBeHex = CGICharWillHex ( *stringLoc );
- if ( charNeedsToBeHex )
- {
- /* the character at strOffset is alpha-numeric */
- strSize++;
- }
- else
- {
- /* the character at strOffset is special (not alpha-numeric) */
- strSize += 3;
- }
- }
-
- /* allocate space for the new encoded string */
- theResult = (char *) MemoryNewPtr ( (strSize + 1), outErr );
- if ( theResult != NULL )
- {
- /* encode theString into the new encoded string (theResult).
- loop through the characters of the old string,
- copying the alpha-numeric chars to the new string,
- but copying in %XX encoded form when non-alpha-numeric. */
- for ( stringLoc = theString, resultLoc = theResult; *stringLoc != nil; stringLoc++, resultLoc++ )
- {
- charNeedsToBeHex = CGICharWillHex ( *stringLoc );
- if ( charNeedsToBeHex )
- {
- /* if the character is alphanumeric just copy it */
- *resultLoc = *stringLoc;
- }
- else
- {
- /* if the character is not alphanumeric, hex encode it */
- CGICharToHex ( *stringLoc, resultLoc );
- /* add the extra two characters for hex encoding to the result offset */
- resultLoc += 2;
- }
- }
-
- /* terminate the string after the last character written */
- *resultLoc = nil;
- }
-
- return theResult;
- } /* CGIEncodeURLChars */
-
-
- /* returns true if theChar will be converted to hex when encoding as an URL string */
- p_export
- Boolean
- CGICharWillHex ( unsigned char theChar )
- {
- return (
- ((theChar >= 'a') && (theChar <= 'z')) || /* 0x60 - 0x7A; a-z */
- ((theChar >= '?') && (theChar <= 'Z')) || /* 0x3F - 0x5A; ? @ A-Z */
- ((theChar >= '-') && (theChar <= ':')) || /* 0x2D - 0x3A; - . / 0-9 : */
- ((theChar >= '\"') && (theChar <= '$')) || /* 0x22 - 0x24; " # $ */
- (theChar == '&') || (theChar == '\'') || /* 0x26 0x27; & ' */
- (theChar == '=') ); /* 0x3D; = */
- } /* CGICharWillHex */
-
-
- /* Converts theChar to percent-hex encoding for URLs, and writes that to theString.
- This assumes that theString is at least 4 bytes long (to have enough room to
- write the hex encoded text). */
- p_export
- void
- CGICharToHex ( unsigned char theChar, char *theString )
- {
- unsigned char chrChunk;
-
- /* start off with a percent symbol to indicate a hex character code */
- theString[0] = '%';
-
- /* mask to get the high 4 bits, then shift them into the lower 4 bits */
- chrChunk = (theChar & 0xF0) >> 4;
- if ( chrChunk > 9 )
- {
- /* chrChunk is a value between A and F in hex */
- theString[1] = (chrChunk - 10) + 'A';
- }
- else
- {
- /* chrChunk is a value between 0 and 9 in hex */
- theString[1] = chrChunk + '0';
- }
-
- /* mask to get the low 4 bits */
- chrChunk = theChar & 0x0F;
- if ( chrChunk > 9 )
- {
- /* chrChunk is a value between A and F in hex */
- theString[2] = (chrChunk - 10) + 'A';
- }
- else
- {
- /* chrChunk is a value between 0 and 9 in hex */
- theString[2] = chrChunk + '0';
- }
- } /* CGICharToHex */
-
-
- /* convert a '/' delimited path to a ':' delimited one, and make sure there's
- one ':' at the start if the path includes folders.
- This function may modify ioPathString.
- ioPathString MUST have an extra byte after its terminating null byte. */
- p_export
- void
- CGIPathToMacPath ( char *ioPathString )
- {
- char * theWorkString;
- char * theSlashOffset;
-
- /* we start with a directory marker, so we won't need to add one. */
- if ( (ioPathString[0] == '/') || (ioPathString[0] == ':') )
- {
- StringConvertCharToChar ( ioPathString, '/', ':' );
- }
- else
- {
- /* check if there are any slashes */
- theSlashOffset = strchr ( ioPathString, '/' );
- if ( theSlashOffset == NULL )
- {
- /* there are no slashes, so nothing no change is needed. */
- }
- else
- {
- /* find the terminating null byte at the end of the string */
- theWorkString = strchr ( ioPathString, '\0' );
- while ( theWorkString >= ioPathString )
- {
- /* the char following the current one is written as a colon if
- this one is a slash, or has this char written to it */
- theWorkString[1] = (theWorkString[0] == '/')? ':' : theWorkString[0];
- theWorkString--;
- }
-
- /* make sure there is a leading colon for the path */
- ioPathString[0] = ':';
- }
- }
- } /* CGIPathToMacPath */
-
-
- /** Memory Allocation Cleanup **/
- #pragma mark -
-
- /* */
- static void
- cgiDisposeHandle ( CGIHdl theCGIHdl )
- {
- my_assert ( theCGIHdl != NULL, "\pcgiDisposeHandle: theCGIHdl is NULL" );
- my_assert ( *theCGIHdl != NULL, "\pcgiDisposeHandle: *theCGIHdl is NULL" );
-
- /* the following is a bunch of statements checking if a parameter has been
- allocated and disposing of it if it has */
-
- #if kCompileWithCGIpath_args
- if ( (*theCGIHdl)->path_args != NULL )
- {
- DisposePtr ( (Ptr)((*theCGIHdl)->path_args) );
- }
- #endif
- #if kCompileWithCGIhttp_search_args
- if ( (*theCGIHdl)->http_search_args != NULL )
- {
- DisposePtr ( (Ptr)((*theCGIHdl)->http_search_args) );
- }
- #endif
- #if kCompileWithCGIpost_args
- if ( (*theCGIHdl)->post_args != NULL )
- {
- DisposePtr ( (Ptr)((*theCGIHdl)->post_args) );
- }
- #endif
- #if kCompileWithCGIscript_name
- if ( (*theCGIHdl)->script_name != NULL )
- {
- DisposePtr ( (Ptr)((*theCGIHdl)->script_name) );
- }
- #endif
- #if kCompileWithCGIreferer
- if ( (*theCGIHdl)->referer != NULL )
- {
- DisposePtr ( (Ptr)((*theCGIHdl)->referer) );
- }
- #endif
-
- #if kCompileWithCGIActionSupport
- if ( (*theCGIHdl)->action_path != NULL )
- {
- DisposePtr ( (Ptr)((*theCGIHdl)->action_path) );
- }
- #endif
-
- #if kCompileWithCGIfull_request
- if ( (*theCGIHdl)->full_request != NULL )
- {
- DisposePtr ( (Ptr)((*theCGIHdl)->full_request) );
- }
- #endif
-
- #if kCompileWithCGIFormHandling
- if ( (*theCGIHdl)->formFields != NULL )
- {
- CGIFormFieldsDispose ( (*theCGIHdl)->formFields );
- }
- #endif
-
- if ( (*theCGIHdl)->responseData != NULL )
- {
- #if kCompileWithCGIResponseDataAsHandle
- DisposeHandle ( (*theCGIHdl)->responseData );
- #else
- DisposePtr ( (Ptr)((*theCGIHdl)->responseData) );
- #endif
- }
-
- DisposeHandle ( (Handle)theCGIHdl );
- } /* cgiDisposeHandle */
-
-
- /** APPLE EVENT SUPPORT **/
- #pragma mark -
-
- /* AppleEvent Handler for the CGI WWWΩ-sdoc event */
- pascal OSErr
- CGIAESearchDoc ( AppleEvent *theAppleEvent, AppleEvent *theReply, long Reference )
- {
- OSErr theErr;
- CGIHdl theCGIHdl;
- #if kCompileWithThreadedAppleEvents
- ThreadID theThread;
- #endif
- #if kCompileWithDebugLogging
- char tempStr[16];
- #endif
-
- /* we're handling a CGI event, so we're more busy now */
- ProcessIsMoreBusy ();
-
- /* reset 'quit on idle time' timer */
- ResetQuitIdleTimer();
-
- #if kCompileWithDebugLogging
- LogStringP ( "\p==================\rCGI Event Recieved\rTICKS: " );
- sprintf ( tempStr, "%d", TickCount() );
- LogStringBreak ( tempStr );
- LogStringBreakP ( "\p------------------" );
- #endif
-
- /* Allocate the CGIHdl data structure - zeroing out its contents. */
- theCGIHdl = (CGIHdl) MemoryNewHandleClear ( sizeof(CGIrecord), &theErr );
- if ( theCGIHdl == NULL )
- {
- /* memory didn't allocate - can't process cgi */
- return theErr;
- }
-
- /* store references to the apple event and reply records */
- (*theCGIHdl)->appleEvent = *theAppleEvent;
- (*theCGIHdl)->replyEvent = *theReply;
-
- // MoveHHi ( (Handle)theCGIHdl );
-
- #if kCompileWithThreadedAppleEvents
- #if kCompileWithThreadsOptional
- if ( gHasThreadMgr )
- {
- #endif
-
- /* It is necessary to suspend the AppleEvent in order to thread its
- processing because of some real weirdness with AEProcessAppleEvent
- not being "reentrant." This means you can't be processing multiple
- Apple Events at the same time, so they have to be 'suspended' if
- you want to deal with more than one (IE. multi-threaded processing). */
- theErr = AESuspendTheCurrentEvent ( theAppleEvent );
- if ( theErr == noErr)
- {
- (*theCGIHdl)->suspended = true;
- /* AppleEvent has been suspended, so we can spawn a thread for processing. */
- theErr = ThreadNewThreadFromPool ( CGIAESearchDocProcessThread,
- theCGIHdl, (void**)NULL, &theThread );
- }
-
- #if kCompileWithThreadsOptional
- }
- #endif
- #endif /* kCompileWithThreadedAppleEvents */
-
- #if kCompileWithThreadedAppleEvents
- #if kCompileWithThreadsOptional
- if ( !gHasThreadMgr || (theErr != noErr) )
- #else
- if ( theErr != noErr )
- #endif
- {
- #endif
-
- /* if threading isn't available, or the attempt to thread failed,
- or the attempt to suspend the AppleEvent failed,
- process the Apple Event without threading. */
- theErr = cgiAESearchDocProcess ( theCGIHdl );
-
- #if kCompileWithThreadedAppleEvents
- }
- else
- {
- /* we suspended the AE, and spawned the thread, now let's start it. */
- //••• YieldToThread ( theThread );
- }
- #endif
-
- return theErr;
- } /* CGIAESearchDoc */
-
-
- /* Entry point for cgi handler thread. */
- #if kCompileWithThreadedAppleEvents
- pascal void *
- CGIAESearchDocProcessThread ( void *threadParam )
- {
- OSErr theErr;
- ThreadID currentThread;
-
- my_assert ( threadParam != NULL,
- "\pCGIAESearchDocProcessThread: the CGI handle (threadParam) is NULL" );
-
- /* The threadParam is used to pass the CGIHdl. */
- theErr = cgiAESearchDocProcess ( (CGIHdl)threadParam );
-
- /* Find the ID of current thread and use DisposeThread to dispose of it so
- that my custom thread termination procedure will be used to recycle
- this thread's allocation for the thread pool. */
- GetCurrentThread ( ¤tThread );
- DisposeThread ( currentThread, (void *)theErr, true );
-
- /* This line below is actually irrelevant, since the DisposeThread call above
- will result in the immediate termination of this thread.
- I keep it in because a return result is needed for the compiler not to
- issue a warning (and I have the "treat all warnings as errors" flag set
- in my compiler, like every programmer should). */
- return (void *)theErr;
- } /* CGIAESearchDocProcessThread */
- #endif
-
- /* Process the CGI WWWΩ-sdoc event.
- theCGIHdl must be valid (non-nil) and unlocked.
- Responsible for ensuring that cgiAEComplete is called so CGIDisposeHandle
- will be called. */
- static OSErr
- cgiAESearchDocProcess ( CGIHdl theCGIHdl )
- {
- OSErr theErr;
- Ptr tempBuffer;
- AppleEvent theAppleEvent;
- /* the 'fieldError' variable is only used if forms with auto-processing are on,
- the method parameter is used, and one or both of the http_search_args and
- post_args are used */
- #if kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod && (kCompileWithCGIhttp_search_args || kCompileWithCGIpost_args)
- short fieldError;
- #endif
-
- my_assert ( theCGIHdl != NULL, "\pcgiAESearchDocProcess: theCGIHdl is NULL" );
- // my_assert ( (*theCGIHdl)->appleEvent != NULL, "\pcgiAESearchDocProcess: theAppleEvent is nil" );
-
- #if __profile__ && __MWERKS__
- gProfileOn = !( ProfilerInit( collectDetailed, bestTimeBase, 20, 5 ) );
- #endif
-
- /* reset 'quit on idle time' timer */
- ResetQuitIdleTimer();
-
- tempBuffer = MemoryNewPtr ( kCGIParamMaxSize, &theErr );
- if ( tempBuffer == NULL )
- {
- goto Exit_Complete;
- }
-
- my_assert ( (HGetState((Handle)theCGIHdl) & kMemoryHandleLockedFlag) == nil,
- "\pcgiAESearchDocProcess: theCGIHdl is already locked!" );
- HLockHi ( (Handle)theCGIHdl );
-
- /* copy the AppleEvent record into a local variable for faster access. */
- theAppleEvent = (*theCGIHdl)->appleEvent;
-
- /* the following section is where the parameters are pulled from the CGI
- Apple Event and allocated in the CGI Handle */
-
- /* '----' - direct parameter:
- path_args - arguments to the URL after a $ */
- #if kCompileWithCGIpath_args
- theErr = AEGetParamString ( &theAppleEvent, '----', &((*theCGIHdl)->path_args),
- (char *)tempBuffer, kCGIParamMaxSize );
- #if kCompileWithCGIAutoDecode
- if ( theErr == noErr )
- {
- CGIDecodeURLChars ( (*theCGIHdl)->path_args );
- }
- #endif
- #endif /* kCompileWithCGIpath_args */
-
- /* 'kfor' - search arguments:
- http_search_args - arguments to the URL after a ? */
- #if kCompileWithCGIhttp_search_args
- theErr = AEGetParamString ( &theAppleEvent, kCGIhttp_search_args,
- &((*theCGIHdl)->http_search_args), (char *)tempBuffer, kCGIParamMaxSize );
- /* leave decoding to after parsing of form fields */
- #endif
-
- /* 'user' - user name:
- username - authenticated user name */
- #if kCompileWithCGIusername
- theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIusername,
- (*theCGIHdl)->username, kCGIMaxusername );
- #endif
-
- /* 'pass' - password:
- password - authenticated password */
- #if kCompileWithCGIpassword
- theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIpassword,
- (*theCGIHdl)->password, kCGIMaxpassword );
- #endif
-
- /* 'frmu' - from user:
- from_user - non-standard. e-mail address of remote user */
- #if kCompileWithCGIfrom_user
- theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIfrom_user,
- (*theCGIHdl)->from_user, kCGIMaxfrom_user );
- #endif
-
- /* 'addr' - client address:
- client_address - IP address or domain name of remote client's host */
- #if kCompileWithCGIclient_address
- theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIclient_address,
- (*theCGIHdl)->client_address, kCGIMaxclient_address );
- #endif
-
- /* 'post' - post arguments:
- post_args - */
- #if kCompileWithCGIpost_args
- theErr = AEGetParamString ( &theAppleEvent, kCGIpost_args,
- &((*theCGIHdl)->post_args), (char *)tempBuffer, kCGIParamMaxSize );
- /* leave decoding to after form parsing */
- #endif
-
- /* 'meth' - HTTP method:
- method - GET, POST, etc. Used to tell if post_args are valid */
- #if kCompileWithCGImethod
- theErr = cgiAEGetParamHTTPMethod ( &theAppleEvent, kCGImethod,
- &((*theCGIHdl)->method), (char *)tempBuffer, kCGIParamMaxSize );
- #endif
-
- /* 'svnm' - server name:
- server_name - name or IP address of this server */
- #if kCompileWithCGIserver_name
- theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIserver_name,
- (*theCGIHdl)->server_name, kCGIMaxserver_name );
- #endif
-
- /* 'svpt' - server port:
- server_port - TCP/IP port number being used by this server */
- #if kCompileWithCGIserver_port
- theErr = AEGetParamShort ( &theAppleEvent, kCGIserver_port,
- &((*theCGIHdl)->server_port), (char *)tempBuffer, kCGIParamMaxSize );
- #endif
-
- /* 'scnm' - script name:
- script_name - URL name of this script */
- #if kCompileWithCGIscript_name
- theErr = AEGetParamString ( &theAppleEvent, kCGIscript_name,
- &((*theCGIHdl)->script_name), (char *)tempBuffer, kCGIParamMaxSize );
- #if kCompileWithCGIAutoDecode
- if ( theErr == noErr )
- {
- CGIDecodeURLChars ( (*theCGIHdl)->script_name );
- }
- #endif
- #endif
-
- /* 'ctyp' - content type:
- content_type - MIME content type of post_args */
- #if kCompileWithCGIcontent_type
- theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIcontent_type,
- (*theCGIHdl)->content_type, kCGIMaxcontent_type );
- #endif
-
- /* 'refr' - referer:
- referer - the URL of the page referencing this document */
- #if kCompileWithCGIreferer
- theErr = AEGetParamString ( &theAppleEvent, kCGIreferer,
- &((*theCGIHdl)->referer), (char *)tempBuffer, kCGIParamMaxSize );
- #endif
-
- /* 'Agnt' - user agent:
- user_agent - the name and version of the WWW client software being used */
- #if kCompileWithCGIuser_agent
- theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIuser_agent,
- (*theCGIHdl)->user_agent, kCGIMaxuser_agent );
- #endif
-
- /* 'Kact' - action name:
- action - the name of the action (CGI or ACGI if not a user defined action) */
- #if kCompileWithCGIActionSupport
- theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIaction,
- (*theCGIHdl)->action, kCGIMaxaction );
- #endif
-
- /* 'Kapt' - action path:
- action_path - path to the action application */
- #if kCompileWithCGIActionSupport
- theErr = AEGetParamString ( &theAppleEvent, kCGIaction_path,
- &((*theCGIHdl)->action_path), (char *)tempBuffer, kCGIParamMaxSize );
- #endif
-
- /* 'Kcip' - client IP address:
- client_ip - the IP address of the client */
- #if kCompileWithCGIclient_ip
- theErr = AEGetParamStringNoAlloc ( &theAppleEvent, kCGIclient_ip,
- (*theCGIHdl)->client_ip, kCGIMaxclient_ip );
- #endif
-
- /* 'Kfrq' - full request:
- full_request - the full text of the request */
- #if kCompileWithCGIfull_request
- theErr = AEGetParamString ( &theAppleEvent, kCGIfull_request,
- &((*theCGIHdl)->full_request), (char *)tempBuffer, kCGIParamMaxSize );
- #endif
-
- /* 'Kcid' - connection ID:
- connection - the ID of the server's connection with a client */
- #if kCompileWithCGISendPartial
- theErr = AEGetParamLong ( &theAppleEvent, kCGIconnection, &((*theCGIHdl)->connection) );
- #endif
-
- /* don't need the buffer any more */
- DisposePtr ( tempBuffer );
-
- //•don't need to check for required parameters because Chuck Shotton says that all
- //parameters are to be optional.
- // /* check that all required parameters were retreived */
- // theErr = AEFuncGotRequiredParams ( &theAppleEvent );
-
- #if kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod
- /* separate the form fields into an array */
- switch ( (*theCGIHdl)->method )
- {
- #if kCompileWithCGIhttp_search_args
- case HTTP_get :
- if ( (*theCGIHdl)->http_search_args != NULL )
- {
- (*theCGIHdl)->formFields = CGIFormFieldsFromArgs (
- (*theCGIHdl)->http_search_args, &((*theCGIHdl)->totalFields),
- &fieldError );
- }
- break;
- #endif
-
- #if kCompileWithCGIpost_args
- case HTTP_post :
- if ( (*theCGIHdl)->post_args != NULL )
- {
- (*theCGIHdl)->formFields = CGIFormFieldsFromArgs (
- (*theCGIHdl)->post_args, &((*theCGIHdl)->totalFields), &fieldError );
- }
- break;
- #endif
- }
- #endif /* kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod */
-
- /* now that the possible need to use them for form fields is over, we can
- decode the search args */
- #if kCompileWithCGIAutoDecode && kCompileWithCGIhttp_search_args
- if ( (*theCGIHdl)->http_search_args != NULL )
- {
- CGIDecodeURLChars ( (*theCGIHdl)->http_search_args );
- }
- #endif
-
- HUnlock ( (Handle)theCGIHdl );
-
- #if kCompileWithDebugLogging
- CGILogData ( theCGIHdl );
- #endif
-
- /* this is where the application specific cgi handling comes into play
- the function "CustomCGIProcess" must be provided by the user of this source code */
- CustomCGIProcess ( theCGIHdl );
-
- Exit_Complete:
-
- #if __profile__ && __MWERKS__
- if ( gProfileOn )
- {
- ProfilerDump ( "\p" kProfileNameStr "-" kProcessorString ".prof" );
- ProfilerTerm ();
- }
- #endif
-
- #if kCompileWithThreadedAppleEvents
- if ( (*theCGIHdl)->suspended )
- {
- /* We're in a suspended AppleEvent, so we'll need to resume the
- AppleEvent to have it complete and return the reply properly. */
- theErr = AEResumeTheCurrentEvent ( &theAppleEvent,
- &((*theCGIHdl)->replyEvent), vCGIAEResumeCompleteUPP, (long)theCGIHdl );
- }
- else
- {
- #endif
-
- /* We weren't suspended, but still need to take care of the AppleEvent
- reply record. */
- theErr = cgiAEComplete ( theCGIHdl );
-
- #if kCompileWithThreadedAppleEvents
- }
- #endif
-
- /* reset 'quit on idle time' timer */
- ResetQuitIdleTimer();
-
- return theErr;
- } /* cgiAESearchDocProcess */
-
-
- /* Call the event completion function (cgiAEComplete) when resuming
- suspended AppleEvents. theReference must be a CGIHdl.
- The result returned from this function will be used as the result of the AppleEvent. */
- #if kCompileWithThreadedAppleEvents
- pascal OSErr
- CGIAEResumeComplete ( const AppleEvent *theAppleEvent, AppleEvent *theReply, long theReference )
- {
- OSErr theErr;
-
- my_assert ( theReference != nil,
- "\pCGIAEResumeComplete: the CGI handle (theReference) is nil" );
-
- theErr = cgiAEComplete ( (CGIHdl)theReference );
-
- return theErr;
- } /* CGIAEResumeComplete */
- #endif
-
- /* Complete the CGI AppleEvent. theCGIHdl must be valid. */
- static OSErr
- cgiAEComplete ( CGIHdl theCGIHdl )
- {
- OSErr theErr;
- #if kCompileWithDebugLogging
- char tempStr[16];
- #endif
-
- my_assert ( theCGIHdl != NULL, "\pcgiAEComplete: theCGIHdl is NULL" );
- my_assert ( (HGetState((Handle)theCGIHdl) & kMemoryHandleLockedFlag) == nil,
- "\pcgiAEComplete: theCGIHdl is already locked!" );
- HLock ( (Handle)theCGIHdl );
-
- if ( (*theCGIHdl)->responseData != NULL )
- {
- #if kCompileWithCGIResponseDataAsHandle
- my_assert ( (HGetState((Handle)((*theCGIHdl)->responseData)) & kMemoryHandleLockedFlag) == nil,
- "\pcgiAEComplete: (*theCGIHdl)->responseData is already locked!" );
- HLock ( (*theCGIHdl)->responseData );
- #endif
-
- /* If the user's "CustomCGIProcess" function set the responseData properly,
- return it. */
- theErr = AEPutParamPtr ( &((*theCGIHdl)->replyEvent), keyDirectObject, typeChar,
- #if kCompileWithCGIResponseDataAsHandle
- *((*theCGIHdl)->responseData),
- #else
- (Ptr)((*theCGIHdl)->responseData),
- #endif
- (*theCGIHdl)->responseSize );
-
- #if kCompileWithCGIResponseDataAsHandle
- HUnlock ( (*theCGIHdl)->responseData );
- #endif
- }
- else
- {
- /* if the user's "CustomCGIProcess" failed to set the responseData properly,
- return an error header. */
- theErr = AEPutParamPtr ( &((*theCGIHdl)->replyEvent), keyDirectObject, typeChar,
- (Ptr)gHTTPHeaderErr, gHTTPHeaderErrSize );
- }
-
- HUnlock ( (Handle)theCGIHdl );
-
- /* give time, then cgiPostProcess */
- ProcessGiveTime ( nil );
- cgiPostProcess ( theCGIHdl );
-
- #if kCompileWithDebugLogging
- CGILogData ( theCGIHdl );
- #endif
-
- /* deallocate memory */
- cgiDisposeHandle ( theCGIHdl );
-
- #if kCompileWithDebugLogging
- LogStringP ( "\p___________________\rCGI Event Completed\rTICKS: " );
- sprintf ( tempStr, "%d", TickCount() );
- LogStringBreak ( tempStr );
- LogStringBreakP ( "\p===================" );
- #endif
-
- /* we're done this CGI event, so we're less busy now */
- ProcessIsLessBusy ();
-
- return theErr;
- } /* cgiAEComplete */
-
-
- /* */
- static void
- cgiPostProcess ( CGIHdl theCGIHdl )
- {
- CustomCGIPostProcess ( theCGIHdl );
- } /* cgiPostProcess */
-
-
- #if kCompileWithCGImethod
- #pragma segment AppleEvents
- /* private function to get an HTTPMethod from an AppleEvent parameter */
- static OSErr
- cgiAEGetParamHTTPMethod ( const AppleEvent *theAppleEvent, AEKeyword theAEKeyword, HTTPMethod *theMethod, char *tempBuffer, long bufferSize )
- {
- OSErr theErr;
- DescType actualType;
- Size actualSize;
- int stringDiff;
-
- my_assert ( theMethod != NULL, "\pcgiAEGetParamHTTPMethod: theMethod ptr is NULL" );
- my_assert ( theAppleEvent != NULL, "\pcgiAEGetParamHTTPMethod: theAppleEvent ptr is NULL" );
- my_assert ( tempBuffer != NULL, "\pcgiAEGetParamHTTPMethod: tempBuffer ptr is NULL" );
-
- theErr = AEGetParamPtr ( theAppleEvent, theAEKeyword, typeChar,
- &actualType, (Ptr)tempBuffer, bufferSize, &actualSize );
- if ( theErr == noErr )
- {
- my_assert ( actualSize <= bufferSize,
- "\pcgiAEGetParamHTTPMethod: actual param size too big" );
-
- /* terminate the buffer with a null byte */
- tempBuffer[actualSize] = nil;
-
- /* compare the buffer with constants to determine the http method used */
-
- stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodPost );
- if ( stringDiff == nil )
- {
- *theMethod = HTTP_post;
- }
- else
- {
- stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodGet );
- if ( stringDiff == nil )
- {
- *theMethod = HTTP_get;
- }
- else
- {
- stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodGetConditional );
- if ( stringDiff == nil )
- {
- *theMethod = HTTP_getConditional;
- }
- else
- {
- *theMethod = HTTP_UNDEFINED;
- }
- }
- }
- }
-
- return theErr;
- } /* cgiAEGetParamHTTPMethod */
- #endif
-
-
- /*** SEND PARTIAL SUPPORT ***/
- #pragma mark -
-
- /* From the WebSTAR 1.2 addendum:
-
- An ACGI has to inform the WebSTAR server that it intends to
- return its results in pieces over a period of time, rather than
- simply lumping it all into the value returned from the WWWWsdoc
- event. WebSTAR examines the results from an ACGI's reply to see
- if it matches the string:
-
- <SEND_PARTIAL>
-
- If this 14 character string is matched exactly (case sensitive),
- then WebSTAR doesn’t send anything to the client and keeps the
- connection open until the timeout period expires or WebSTAR
- receives data via the Send Partial event for that con-nection.
-
- Send Partial events received before the <SEND_PARTIAL> response
- to the sdoc
-
- event are also a legal way to indicate that server push
- functions are to be performed for a given connection.
-
- As long as Send Partial events are received for a given
- connection, the timeout timer is restarted and the data is sent
- to the client. If the Send Partial event’s “more” parameter is
- FALSE, the server closes the connection and assumes that the
- ACGI has finished sending data.
-
- To send events back to WebSTAR from a language like C or Pascal,
- you must extract and save the “from” Apple event attribute from
- the reply event sent to your WWWWsdoc handler. The “from” event
- contains the AEAddressDesc used to address the “SPar” events to
- WebSTAR.
-
- Here's the general flow of events
-
- 1. A WWW client sends an ACGI URL request to WebSTAR.
-
- 2. WebSTAR sends a WWWWsdoc event to the ACGI, passing the
- connection ID.
-
- 3. The ACGI decides it needs to return data in pieces, so it
- replies to the sdoc event with the string <SEND_PARTIAL> and
- saves the connection ID.
-
- 4. WebSTAR sees that the ACGI wants to return partial data, so
- it sets a flag indi-cating that the connection should be checked
- periodically for timeouts and resets the timer.
-
- 5. The ACGI sends a Send Partial event to WebSTAR with the first
- chunk of data, the connection ID, and the “more” flag set to
- TRUE, indicating more data to come.
-
- 6. WebSTAR handles the Send Partial event, finds the requested
- connection, and queues up the data for transmission to the
- client (allowing the thread owning the connection to schedule
- the transmission.) WebSTAR then resets the time-out for the
- connection.
-
- Steps 5 and 6 repeat at whatever interval the client decides
- until the “more” parameter is false. WebSTAR receives the final
- Send Partial event with the “more” flag set to false, sends the
- accompanying data, and closes the client connection. */
-
- /* The connection ID is found in theCGIHdl as the 'connection' field.
- Connection is used to identify the particular connection on which to send
- the data.
- The data to be sent is pointed to by theData, which is dataSize bytes long.
- If sendMore is true, there is more data to be sent after processing this
- event. If it is false, the server will terminate the connection after
- sending the data from this event.
- theCGIHdl should not be locked when it is passed to this function.
- */
- #if kCompileWithCGISendPartial
- #pragma segment AppleEvents
- p_export
- OSErr
- CGIAESendPartial ( CGIHdl theCGIHdl, char *theData, long dataSize, Boolean sendMore )
- {
- OSErr theErr;
- DescType descApp;
- AEAddressDesc targetAddress;
- AppleEvent theAppleEvent;
- AppleEvent reply;
- short errNum;
- DescType returnedType;
- long actualSize;
-
- my_assert ( theCGIHdl != NULL, "\pCGIAESendPartial: theCGIHdl is NULL" );
- my_assert ( theData != NULL, "\pCGIAESendPartial: theData is NULL" );
-
- /* ••• need to change this to use the process ID from the server */
- /* create the application descriptor */
- descApp = kAEClassCGI;
- theErr = AECreateDesc ( typeApplSignature, &descApp, sizeof(DescType),
- &targetAddress );
- if ( theErr != noErr )
- {
- goto EXIT_AESEND;
- }
-
- /* create the apple event record */
- theErr = AECreateAppleEvent ( kAEClassCGI, kMyAESendPartial, &targetAddress,
- kAutoGenerateReturnID, kAnyTransactionID, &theAppleEvent );
- if ( theErr != noErr )
- {
- goto EXIT_AESEND;
- }
-
- /* put the data */
- theErr = AEPutParamPtr ( &theAppleEvent, kCGIPartialData, typeChar,
- theData, dataSize );
- if ( theErr != noErr )
- {
- goto EXIT_AESEND;
- }
-
- my_assert ( (HGetState((Handle)theCGIHdl) & kMemoryHandleLockedFlag) == nil,
- "\pCGIAESendPartial: theCGIHdl is already locked!" );
- HLockHi ( (Handle)theCGIHdl );
- /* put the connection id */
- theErr = AEPutParamPtr ( &theAppleEvent, kConnectionIDKeyword, typeLongInteger,
- &((*theCGIHdl)->connection), sizeof(long) );
- if ( theErr != noErr )
- {
- goto EXIT_AESEND;
- }
- HUnlock ( (Handle)theCGIHdl );
-
- /* put the more value */
- theErr = AEPutParamPtr ( &theAppleEvent, kMoreKeyword, typeBoolean, &sendMore,
- sizeof(Boolean) );
- if ( theErr != noErr )
- {
- goto EXIT_AESEND;
- }
-
- /* send the apple event */
- theErr = AESend ( &theAppleEvent, &reply, kAEWaitReply + kAENeverInteract /*5-14*/,
- kAENormalPriority, kAEMyTimeoutInTicks, gAEIdleUPP, NULL );
- if ( theErr != noErr )
- {
- goto EXIT_AESEND;
- }
-
- /* extract the error reply */
- errNum = noErr;
- theErr = AEGetParamPtr ( &reply, keyErrorNumber, typeSMInt, &returnedType, &errNum,
- sizeof(errNum), &actualSize );
- if ( (theErr != noErr) && (theErr != errAEDescNotFound) )
- {
- goto EXIT_AESEND;
- }
-
- theErr = errNum;
-
- EXIT_AESEND:
-
- AEDisposeDesc ( &targetAddress );
- AEDisposeDesc ( &theAppleEvent );
-
- return theErr;
- } /* CGIAESendPartial */
- #endif /* kCompileWithCGISendPartial */
-
-
- /** LOGGING **/
- #pragma mark -
-
- /* */
- p_export
- void
- CGILogData ( CGIHdl theCGIHdl )
- {
- #if kCompileWithCGIserver_port || kCompileWithCGISendPartial || kCompileWithCGIFormHandling
- char tempStr[256];
- #endif
- #if kCompileWithCGIFormHandling
- long counter;
- #endif
-
- LogStringBreakP ( "\p=== CGI PARAMETERS ===" );
-
- #if kCompileWithCGIpath_args
- if ( (*theCGIHdl)->path_args != NULL )
- {
- LogString ( "path_args:" );
- LogStringBreak ( (*theCGIHdl)->path_args );
- }
- #endif
-
- #if kCompileWithCGIhttp_search_args
- if ( (*theCGIHdl)->http_search_args != NULL )
- {
- LogString ( "http_search_args:" );
- LogStringBreak ( (*theCGIHdl)->http_search_args );
- }
- #endif
-
- #if kCompileWithCGIusername
- LogString ( "username:" );
- LogStringBreak ( (*theCGIHdl)->username );
- #endif
-
- #if kCompileWithCGIpassword
- if ( (*theCGIHdl)->password != NULL )
- {
- LogString ( "password:" );
- LogStringBreak ( (*theCGIHdl)->password );
- }
- #endif
-
- #if kCompileWithCGIfrom_user
- if ( (*theCGIHdl)->from_user != NULL )
- {
- LogString ( "from_user:" );
- LogStringBreak ( (*theCGIHdl)->from_user );
- }
- #endif
-
- #if kCompileWithCGIclient_address
- if ( (*theCGIHdl)->client_address != NULL )
- {
- LogString ( "client_address:" );
- LogStringBreak ( (*theCGIHdl)->client_address );
- }
- #endif
-
- #if kCompileWithCGIpost_args
- if ( (*theCGIHdl)->post_args != NULL )
- {
- LogString ( "post_args:" );
- LogStringBreak ( (*theCGIHdl)->post_args );
- }
- #endif
-
- #if kCompileWithCGImethod
- LogString ( "method:" );
- switch ( (*theCGIHdl)->method )
- {
- case HTTP_get :
- LogStringBreak ( kCGIHTTPMethodGet );
- break;
-
- case HTTP_post :
- LogStringBreak ( kCGIHTTPMethodPost );
- break;
-
- case HTTP_getConditional :
- LogStringBreak ( kCGIHTTPMethodGetConditional );
- break;
-
- default :
- LogStringBreak ( "<undefined>" );
- break;
- }
- #endif
-
- #if kCompileWithCGIserver_name
- if ( (*theCGIHdl)->server_name != NULL )
- {
- LogString ( "server_name:" );
- LogStringBreak ( (*theCGIHdl)->server_name );
- }
- #endif
-
- #if kCompileWithCGIserver_port
- LogString ( "server_port:" );
- sprintf ( tempStr, "%d", (*theCGIHdl)->server_port );
- LogStringBreak ( tempStr );
- #endif
-
- #if kCompileWithCGIscript_name
- if ( (*theCGIHdl)->script_name != NULL )
- {
- LogString ( "script_name:" );
- LogStringBreak ( (*theCGIHdl)->script_name );
- }
- #endif
-
- #if kCompileWithCGIcontent_type
- if ( (*theCGIHdl)->content_type != NULL )
- {
- LogString ( "content_type:" );
- LogStringBreak ( (*theCGIHdl)->content_type );
- }
- #endif
-
- #if kCompileWithCGIreferer
- if ( (*theCGIHdl)->referer != NULL )
- {
- LogString ( "referer:" );
- LogStringBreak ( (*theCGIHdl)->referer );
- }
- #endif
-
- #if kCompileWithCGIuser_agent
- if ( (*theCGIHdl)->user_agent != NULL )
- {
- LogString ( "user_agent:" );
- LogStringBreak ( (*theCGIHdl)->user_agent );
- }
- #endif
-
- #if kCompileWithCGIActionSupport
- LogString ( "action:" );
- LogStringBreak ( (*theCGIHdl)->action );
- if ( (*theCGIHdl)->action_path != NULL )
- {
- LogString ( "action_path:" );
- LogStringBreak ( (*theCGIHdl)->action_path );
- }
- #endif
-
- #if kCompileWithCGIclient_ip
- LogString ( "client_ip:" );
- LogStringBreak ( (*theCGIHdl)->client_ip );
- #endif
-
- #if kCompileWithCGIfull_request
- if ( (*theCGIHdl)->full_request != NULL )
- {
- LogString ( "full_request:" );
- LogStringBreak ( (*theCGIHdl)->full_request );
- }
- #endif
-
- #if kCompileWithCGISendPartial
- LogString ( "connection:" );
- sprintf ( tempStr, "%d", (*theCGIHdl)->connection );
- LogStringBreak ( tempStr );
- #endif
-
- #if kCompileWithCGIFormHandling
- LogString ( "totalFields:" );
- sprintf ( tempStr, "%d", (*theCGIHdl)->totalFields );
- LogStringBreak ( tempStr );
-
- if ( (*theCGIHdl)->totalFields > nil )
- {
- LogStringBreak ( "formFields:" );
- for ( counter = 0; counter < (*theCGIHdl)->totalFields; counter++ )
- {
- LogStringP ( "\p\t" ); /* put a tab character at the begining */
- LogStringAndSeparator ( (((*theCGIHdl)->formFields)[counter]).name, '\t' );
- LogStringBreak ( (((*theCGIHdl)->formFields)[counter]).value );
- }
- }
- #endif
-
- LogStringBreakP ( "\p=== end cgi parameters ===" );
-
- } /* CGILogData */
-
-
- #endif /* kCompileWithCGICode */
-
- /*** EOF ***/
-